package services.mes

import models.mes.MesOrdersRepository
import play.api.Logging
import play.api.libs.json._
import play.api.libs.functional.syntax._
import services.kingdee.KingdeePostV2

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import javax.inject.Inject
import scala.collection.mutable
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}

class MoreMesBaseDoc @Inject()(
                                moreMesPost: MoreMesPost,
                                mesRepo: MesOrdersRepository,
                                kingdeePost: KingdeePostV2
                              )(implicit exec: ExecutionContext)
  extends Logging {

  private var mesPost = moreMesPost.setConfig("")
  private var kdPost = kingdeePost.setKdAppConfig(mesPost.kdAppConfig.dCID)
  private var stepTime = 60 // 60 Minutes

  def setStepTime(t: String) = {
    val time_str = "[DdHhMmSs]".r.findFirstIn(t).getOrElse("M").toUpperCase
    val time_num = "\\d+".r.findAllIn(t).mkString.toInt
    this.stepTime = time_str match {
      case "D" => time_num * 24 * 60
      case "H" => time_num * 60
      case "S" => time_num / 60
      case _ => time_num
    }
    this
  }

  def setConfig(config: String) = {
    this.mesPost = moreMesPost.setConfig(config)
    this.kdPost = kingdeePost.setKdAppConfig(mesPost.kdAppConfig.dCID)
    this
  }

  def getMaterialView(code: String) = {
    val response = kdPost.view(
      "BD_MATERIAL",
      Json.obj("Number" -> code)
    )
    response.flatMap { res =>
      logger.debug(s"getMaterialView($code):${res.take(200)}^^^^^^")
      val bool = {
        val i = res.indexOf("\"DocumentStatus\"")
        if (i >= 0 && res.length > i + 25) {
          res.substring(i, i + 25).indexOf("\"C\"") > 0
        }
        else false
      }
      if (bool) {
        val js = Json.parse(res)
        val result = (js \ "Result" \ "Result").as[JsValue]
        putMaterial2Mes(data = result)
      }
      else {
        println("ZZZZZZZZZZZZZZ")
        Future(res)
      }
    }
  }

  private def changeMaterialInfo(src: JsValue) = {
    import play.api.libs.json._
    import play.api.libs.json.Reads._
    lazy val source_desc = (code: String) => code match {
      case "1" => "外购"
      case "2" => "自制"
      case "3" => "委外"
      case _ => ""
    }
    lazy val item_attr = (code: String) => code.head match {
      case 'Y' => 1
      case 'A' => 2
      case 'B' => 3
      case 'C' => 1
      case 'E' => 3
      case _ => 1
      // 1: 原材料      2: 成品      、3: 半成品
    }
    val materialBase = src("MaterialBase")(0)
    logger.debug(s"materialBase:$materialBase")
    val materialProd = src("MaterialProduce")(0)
    val materialPurch = src("MaterialPurchase")(0)
    val materialStock = src("MaterialStock")(0)
    val materialPlan = src("MaterialPlan")(0)
    val materialGroup = (src \ "MaterialGroup")
      .asOpt[JsObject]
      .getOrElse(
        Json.obj(
          "Number" -> "Number",
          "Name" -> Json.arr(
            Json.obj("Value" -> "Value")
          )
        )
      )
    logger.debug(materialGroup.toString())
    val item_comm = Json.obj(
      "DATA_AUTH" -> "1",
      "USER_ID" -> "A0023",
      "ITEM_CODE" -> src("Number"), // VARCHAR2100是物料编码
      "ITEM_NAME" -> src("Name")(0)("Value"), // VARCHAR2500是物料名称
      // VARCHAR2500是物料规格
      "ITEM_SPEC" -> {
        val s = src("Specification")(0)("Value").as[String]
        if (s.length <= 1) "null" else s
      },
      // VARCHAR210否生产分类(1:原材料 2:成品、3:半成品)
      "ITEM_ATTR" -> item_attr(src("Number").as[String]),
      // VARCHAR220否物料来源描述（1.采购 2.自制 3.委外，虚拟件）CODE（以ERP实际为准）
      "ITEM_SOURCE" -> src("MaterialBase")(0)("ErpClsID"),
      // VARCHAR250否物料来源（1.采购 2.自制 3.委外）描述（以ERP实际为准）
      "ITEM_SOURCE_DESC" -> source_desc(src("MaterialBase")(0)("ErpClsID").as[String]),
      "CUST_CODE" -> src("MnemonicCode").as[String], // VARCHAR2100否客户代码
      "MIN_PACK" -> materialPlan("IncreaseQty"), // NUMBER否最小包装量
      // "STOCK_CYCLE"->  "",  //NUMBER否质保期（天），ERP暂时没有该数据
      // "CYCLE_REMIND"->  "",  //NUMBER否过期提醒天数，ERP暂时没有该数据
      "UNIT" -> materialBase("BaseUnitId")("Number"), // VARCHAR220否基本单位CODE
      // VARCHAR220否基本单位描述
      "UNIT_DESC" -> materialBase("BaseUnitId")("Name")(0)("Value"),
      // "IS_IQC_FREE"->  "",  //VARCHAR22否是否免检（Y/N），ERP没有该字段，默认给N
      // "DULL_CYCLE"->  "",  //NUMBER否呆滞周期（天），ERP暂时没有该数据
      "ITEM_GENRE" -> materialGroup("Number"), // VARCHAR250 是 物料分组编码
      // VARCHAR2100 是 物料分组名称
      "ITEM_GENRE_NAME" -> materialGroup("Name")(0)("Value"),
      // VARCHAR250 否 存货类别编码
      "ITEM_TYPE" -> materialBase("CategoryID")("Number"),
      // VARCHAR2100 否 存货类别名称
      "ITEM_TYPE_NAME" -> materialBase("CategoryID")("Name")(0)("Value"),
      // VARCHAR220 否 生产单位编码
      "PRODUCTION_UNIT" -> materialProd("ProduceUnitId")("Number"),
      // VARCHAR220 否 生产单位描述
      "PRODUCTION_UNIT_DESC" -> materialProd("ProduceUnitId")("Name")(0)("Value"),
      // VARCHAR220 否 库存单位编码
      "STOCK_UNIT" -> materialStock("StoreUnitID")("Number"),
      // VARCHAR220 否 库存单位描述
      "STOCK _UNIT_DESC" -> materialStock("StoreUnitID")("Name")(0)("Value"),
      // VARCHAR220 否 采购单位编码
      "P0_UNIT" -> materialPurch("PurchaseUnitID")("Number"),
      // VARCHAR2	20	否	采购单位描述
      "P0_UNIT_DESC" -> materialPurch("PurchaseUnitID")("Name")(0)("Value")
    )
    val item_weight = if (materialBase("WEIGHTUNITID").asOpt[JsObject].nonEmpty) {
      item_comm.transform(
        __.json.update(
          __.read[JsObject].map {
            _ ++ Json.obj(
              "NET_WEIGHT" -> materialBase("NETWEIGHT"),
              // NUMBER否毛重
              "GROSS_WEIGHT" -> materialBase("GROSSWEIGHT"),
              // VARCHAR220 否 重量单位编码
              "WEIGHT_UNIT" -> materialBase("WEIGHTUNITID")("Number"),
              // VARCHAR220 否 重量单位描述
              "WEIGHT_UNIT_DESC" -> materialBase("WEIGHTUNITID")("Name")(0)("Value")
            )
          }
        )
      ).asOpt.get
    } else item_comm
    logger.debug("item_weight => " + item_weight.toString)
    val item_volumeunit = if (materialBase("VOLUMEUNITID").asOpt[JsObject].nonEmpty) {
      item_weight.transform(
        __.json.update(
          __.read[JsObject].map {
            _ ++ Json.obj(
              // VARCHAR220 否 尺寸单位编码
              "SIZE_UNIT" -> materialBase("VOLUMEUNITID")("Number"),
              // VARCHAR220 否 尺寸单位描述
              "SIZE_UNIT_DESC" -> materialBase("VOLUMEUNITID")("Name")(0)("Value"),
              // NUMBER否长
              "ITEM_LENGTH" -> materialBase("LENGTH"),
              // NUMBER否宽
              "ITEM_WIDE" -> materialBase("WIDTH"),
              // NUMBER否高
              "ITEM_HIGHT" -> materialBase("HEIGHT")
            )
          }
        )
      ).asOpt.get
    } else item_weight
    logger.debug("item_volumeunit => " + item_volumeunit.toString)
    Json.obj("dataList" -> item_volumeunit)
  }

  private def putMaterial2Mes(data: JsValue) = {
    val body = changeMaterialInfo(data)
    //    logger.debug(s"changeMOInfo(data):${putData.toString()}")
    logger.debug(s"changeMOInfo(data):body=${body.toString()}")
    mesPost.saveItem(body).map(
      res => {
        logger.info(s"putMaterial2Mes():${body.toString()} => $res")
        res
      }
    )
  }

  def getMaterialList(date: String = "") = {
    val qs = if (date.equals("")) {
      s"FApproveDate>=to_date('${
        LocalDateTime.now()
          .minusMinutes(stepTime)
          .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
      }','yyyy-mm-dd HH24:MI:SS') and FDocumentStatus ='C'"
    }
    else if (date.matches("\\d{8}")) {
      s"FModifyDate >=to_date('$date','yyyymmdd') and FDocumentStatus ='C'"
    }
    else {
      s"FNumber='$date' and FDocumentStatus ='C'"
    }
    logger.debug(s"getProductOrder FilterString: $qs")
    val lst: mutable.ArrayBuffer[String] = mutable.ArrayBuffer()
    var startRow = 0
    val limit = 10
    var isEnd = true
    while (isEnd) {
      val para = Json.obj(
        "FormId" -> "BD_MATERIAL",
        "FieldKeys" -> "FNumber,FDocumentStatus",
        "FilterString" -> qs,
        "OrderString" -> "FNumber",
        "TopRowCount" -> 0,
        "StartRow" -> lst.size,
        "Limit" -> limit
      )
      val res = Await.result(kdPost.executeBillQuery(para), Duration.Inf)
      logger.debug(s"${res.getClass}\tgetMaterialList: ${res}")
      val arr = Json.parse(res).as[Array[JsArray]].map(
        f => f.value(0).as[String]
      )
      isEnd = arr.length == limit
      for (f <- arr) {
        //        getMOView(f)
        lst.append(f)
      }
    }
    lst.foreach(f => getMaterialView(f))
    lst
  }


  private def changeBomInfo(src: JsValue) = {
    case class Item(
                     var ITEM_CODE: String = "",
                     var ITEM_UNIT: String = "",
                     var ITEM_QTY_MOL: Double = 0.0,
                     var ITEM_QTY_DENOMINATOR: Double = 0.0,
                     var LOSS_RATE: Double = 0.0,
                     var REPLACE_ITEM_LIST: mutable.ArrayBuffer[JsValue] = mutable.ArrayBuffer()
                   )

    val entity = src("TreeEntity").as[JsArray].value
    var items: Map[Int, Item] = Map()
    entity.foreach {
      bom => {
        val whcode = if ((bom \ "STOCKID").asOpt[JsObject].isEmpty) None else bom("STOCKID")("Number")
        val itemInfo = bom("MATERIALIDCHILD")
        val groupKey = bom("ReplaceGroup").as[Int]
        if (!(items.contains(groupKey))) {
          items += (bom("ReplaceGroup").as[Int] -> Item())
        }
        val sunhao = if (bom("DOSAGETYPE").as[String].equals("2")) {
          bom("FSCRAPRATE").as[Double] / 100.0
        } else bom("FIXSCRAPQTY").as[Double]
        val item_code = (itemInfo \ "Number").as[String]
        val item_unit =  (itemInfo \ "MaterialStock" \ 0 \ "StoreUnitID" \ "Number").as[String]
        val item_qty_mol =  bom("NUMERATOR").as[Double]
        val item_qty_den = bom("DENOMINATOR").as[Double]
        println(item_code,item_unit,item_qty_mol,item_qty_den)
        if (bom("ParentRowId").as[String].length > 10) {
          items(groupKey).REPLACE_ITEM_LIST +=Json.obj(
            "REPLACE_ITEM_CODE" -> item_code ,
            "REPLACE_ITEM_UNIT" -> item_unit,
            "REPLACE_ITEM_QTY_MOL" -> item_qty_mol,
            "REPLACE_ITEM_QTY_DENOMINATOR" ->  item_qty_den,
            "REPLACE_LOSS_RATE" -> sunhao
          )
        } else {
          items(groupKey).ITEM_CODE = item_code
          items(groupKey).ITEM_UNIT = item_unit
          items(groupKey).ITEM_QTY_MOL = item_qty_mol
          items(groupKey).ITEM_QTY_DENOMINATOR = item_qty_den
          items(groupKey).LOSS_RATE = sunhao
        }
      }
    }
    implicit val testJsonWrites: Writes[Item] = (
      (JsPath \ "ITEM_CODE").write[String] and
        (JsPath \ "ITEM_UNIT").write[String] and
        (JsPath \ "ITEM_QTY_MOL").write[Double] and
        (JsPath \ "ITEM_QTY_DENOMINATOR").write[Double] and
        (JsPath \ "LOSS_RATE").write[Double] and
        (JsPath \ "REPLACE_ITEM_LIST").write[mutable.ArrayBuffer[JsValue]]
      ) (unlift(Item.unapply))
    val lst = items.map(f => {
      Json.toJson(f._2)
    }).toSeq
    println(items)
    Json.obj(
      "dataList" -> Json.obj(
        "DATA_AUTH" -> "1",
        "MODEL_CODE" -> src("MATERIALID")("Number"),
        "MODEL_VER" -> src("Number"),
        "ITEM_LIST" -> lst
      )
    )
  }

  private def putBom2Mes(data: JsValue) = {
    val body = changeBomInfo(data)
//        logger.debug(s"changeBoMInfo(data):src=${data.toString()}")
    logger.debug(s"changeBomInfo(data):body=${body.toString()}")
    mesPost.saveBom(body).map(
      res => {
        logger.info(s"putBom2Mes():${body.toString()} => $res")
        res
      }
    )
  }

  def getBomView(code: String) = {
    val response = kdPost.view(
      "ENG_BOM",
      Json.obj("Number" -> code)
    )
    response.flatMap { res =>
      logger.debug(s"getProduceBomView($code):${res.take(200)}^^^^^^")
      val bool = {
        val i = res.indexOf("\"DocumentStatus\"")
        if (i >= 0 && res.length > i + 25) {
          res.substring(i, i + 25).indexOf("\"C\"") > 0
        }
        else false
      }
      if (bool) {
        val js = Json.parse(res)
        val result = (js \ "Result" \ "Result").as[JsValue]
        putBom2Mes(data = result)
      }
      else {
        println("ZZZZZZZZZZZZZZ")
        Future(res)
      }
    }
  }

  def getProduceBomList(date: String = "") = {
    val qs = if (date.equals("")) {
      s"FApproveDate>=to_date('${
        LocalDateTime.now()
          .minusMinutes(stepTime)
          .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
      }','yyyy-mm-dd HH24:MI:SS') and FDocumentStatus ='C'"
    }
    else if (date.matches("\\d{8}")) {
      s"FModifyDate >=to_date('$date','yyyymmdd') and FDocumentStatus ='C'"
    }
    else {
      s"FNumber='$date' and FDocumentStatus ='C'"
    }
    logger.debug(s"getProduceBomList FilterString: $qs")
    val lst: mutable.ArrayBuffer[String] = mutable.ArrayBuffer()
    var startRow = 0
    val limit = 10
    var isEnd = true
    while (isEnd) {
      val para = Json.obj(
        "FormId" -> "ENG_BOM",
        "FieldKeys" -> "FMATERIALID.fnumber,FNumber,FName,FApproveDate",
        "FilterString" -> qs,
        "OrderString" -> "FNumber",
        "TopRowCount" -> 0,
        "StartRow" -> lst.size,
        "Limit" -> limit
      )
      val res = Await.result(kdPost.executeBillQuery(para), Duration.Inf)
      logger.debug(s"${res.getClass}\tgetProduceBomList: ${res}")
      val arr = Json.parse(res).as[Array[JsArray]].map(
        f => f.value(0).as[String]
      )
      isEnd = arr.length == limit
      for (f <- arr) {
        //        getMOView(f)
        lst.append(f)
      }
    }
    lst.foreach(f => getBomView(f))
    lst
  }

  private def putSupplier2Mes(body: JsValue) = {
    logger.debug(s"putSupplier2Mes(body):body=${body.toString()}")
    mesPost.saveSupplier(body).map(
      res => {
        logger.info(s"putSupplier2Mes():${body.toString()} => $res")
        res
      }
    )
  }

  def getSupplierList(date: String = "") = {
    val qs = if (date.equals("")) {
      s"FAuditDate>=to_date('${
        LocalDateTime.now()
          .minusMinutes(stepTime)
          .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
      }','yyyy-mm-dd HH24:MI:SS') and FDocumentStatus ='C'"
    }
    else if (date.matches("\\d{8}")) {
      s"FModifyDate >=to_date('$date','yyyymmdd') and FDocumentStatus ='C'"
    }
    else {
      s"fnumber='$date'"
    }
    logger.debug(s"getSupplierList FilterString: $qs")
    val lst: mutable.ArrayBuffer[JsObject] = mutable.ArrayBuffer()
    var startRow = 0
    val limit = 10
    var isEnd = true
    while (isEnd) {
      val para = Json.obj(
        "FormId" -> "BD_Supplier",
        "FieldKeys" -> "FNumber,FName,FShortName,FSupplyClassify,FBusinessStatus,FForbidDate,FContact,FTel,FMobile,FAddress",
        "FilterString" -> qs,
        "OrderString" -> "FNumber",
        "TopRowCount" -> 0,
        "StartRow" -> lst.size,
        "Limit" -> limit
      )
      val res = Await.result(kdPost.executeBillQuery(para), Duration.Inf)
      logger.debug(s"${res.getClass}\tgetSupplierList: ${res}")
      val arr = Json.parse(res).as[Array[JsArray]].map(
        //    # "FNumber,FName,FShortName,FSupplyClassify,FBusinessStatus,FForbidDate,FContact,FTel,FMobile,FAddress"
        //    # 0编号，1名称，2简称， 3类型， 4业务状态，5禁用时间，6联系人，7电话，8手机，9地址
        //    # ["S010001", "湖北恒品源科技股份有限公司", " ", "CG", "A", None, None, None, None, " "]
        data => Json.obj("dataList" -> Json.obj(
          "SUPPLIER_CODE" -> data.value(0).as[String],
          "SUPPLIER_NAME" -> data.value(1).as[String],
          "SUPPLIER_SHORTNAME" -> data.value(2).as[String],
          "SUPPLIER_TYPE" -> "A",
          "SUPPLIER_STATUS" -> {
            if (data.value(4).as[String].equals("A")) "1" else "0"
          },
          "EXPIRY_DATE" -> data.value(5).asOpt[String],
          "SUPPLIER_CONPEOPLE" -> data.value(6).asOpt[String],
          "SUPPLIER_PHONE" -> {
            lazy val tel = data.value(7).asOpt[String]
            val mobile = data.value(8).asOpt[String]
            if (tel.nonEmpty) tel.get
            else mobile
          },
          "SUPPLIER_ADDRESS" -> data.value(9).as[String],
          "DATA_AUTH" -> "1"
        ))
      )
      isEnd = arr.length == limit
      for (f <- arr) {
        //        getMOView(f)
        lst.append(f)
      }
    }
    //    lst.foreach(f => putSupplier2Mes(f))
    if (lst.nonEmpty) {
      lst.map(f => putSupplier2Mes(f)).last
    } else {
      Future(s"not find $date")
    }

  }

  def runTask() = {
    getMaterialList()

    getProduceBomList()

    getSupplierList()

  }
}
